package com.atguigu.gmall.realtime.util;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.realtime.common.GmallConstant;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisFuture;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @Author lzc
 * @Date 2023/8/12 09:14
 */
public class RedisUtil {
    
    private static final JedisPool pool;
    
    static {
        
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(300);  // 最多提供 300 个客户端
        config.setMaxIdle(20);  // 最大空闲
        config.setMinIdle(10); // 最小空闲
        config.setMaxWaitMillis(10000); //当连接池没有空闲连接的时候, 最大等待时间
        config.setTestOnCreate(true);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        
        pool = new JedisPool(config, "hadoop162", 6379);
    }
    
    public static Jedis getRedisClient() {
        Jedis jedis = pool.getResource();
        jedis.select(4);  // 选择指定的数据库
        
        return jedis;
    }
    
    public static void closeRedisClient(Jedis redisClient) {
        if (redisClient != null) {
            // 1. 如果客户端是通过 new jedis 出来的,则是关闭
            // 2. 如果客户端是通过连接池获取的,则是归还
            redisClient.close();
        }
    }
    
    /**
     * 根据传入的条件从 redis 读取维度
     *
     * @param redisClient redis 的客户端
     * @param tableName   表名
     * @param id          id 的值
     * @param tClass
     * @return 把读到维度值封装到 T 类型的对象中. 如果返回值是 null,表示没有查到对应的维度信息
     */
    public static <T> T getOneRow(Jedis redisClient,
                                  String tableName,
                                  String id,
                                  Class<T> tClass) {
        String key = getKey(tableName, id);
        String jsonStr = redisClient.get(key);
        if (jsonStr != null) {  // 当 jsonStr 是非 null 的时候,把这个 json 字符串解析成 T 类型的对象
            return JSON.parseObject(jsonStr, tClass);
        }
        return null;
    }
    
    /**
     * 把指定的维度数据写入到 redis 中
     *
     * @param redisClient
     * @param tableName
     * @param id
     * @param dim
     * @param <T>
     */
    public static <T> void writeOneRow(Jedis redisClient,
                                       String tableName,
                                       String id,
                                       T dim) {
        String key = getKey(tableName, id);
        /*redisClient.set(key, JSON.toJSONString(dim));
        // 给 key 设置 ttl
        redisClient.expire(key, GmallConstant.TWO_DAY_SECONDS);*/
        
        redisClient.setex(key, GmallConstant.TWO_DAY_SECONDS, JSON.toJSONString(dim));
    }
    
    
    private static String getKey(String tableName, String id) {
        return tableName + ":" + id;
    }
    
    public static void deleteOneRowCache(Jedis redisClient, String tableName, String id) {
        redisClient.del(getKey(tableName, id));
    }
    
    
    public static RedisClient getAsyncRedisClient() {
        return RedisClient.create("redis://hadoop162:6379/4");
    }
    
    
    public static StatefulRedisConnection<String, String> getAsyncRedisConnection(RedisClient asyncRedisClient) {
        return asyncRedisClient.connect();
    }
    
    /**
     * TODO
     * 异步的方式从 redis 去维度数据
     *
     * @param asyncRedisConnection 异步连接
     * @param tableName            表名
     * @param id                   id 的值
     * @param tClass               封装到数据类型的 class
     * @param <T>                  要放在的数据的类型
     * @return 把查到的维度数据封装到 T 类型的对象中
     */
    public static <T> T asyncGetOneRow(StatefulRedisConnection<String, String> asyncRedisConnection,
                                       String tableName,
                                       String id,
                                       Class<T> tClass) {
        // 获取一个异步命令
        RedisAsyncCommands<String, String> commands = asyncRedisConnection.async();
        String key = getKey(tableName, id);
        RedisFuture<String> future = commands.get(key);
        try {
            String jsonStr = future.get(); // 会阻塞: 得到具体的结果
            if (jsonStr != null) {
                return JSON.parseObject(jsonStr, tClass);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return null;
    }
    
    
    /**
     * TODO
     * 异步把指定维度写入到 redis 中.
     *
     * @param asyncRedisConnection
     * @param tableName
     * @param id
     * @param dim
     * @param <T>
     */
    public static <T> void asyncWriteOneRow(StatefulRedisConnection<String, String> asyncRedisConnection,
                                            String tableName,
                                            String id,
                                            T dim) {
    
        RedisAsyncCommands<String, String> commands = asyncRedisConnection.async();
        commands.setex(getKey(tableName, id), GmallConstant.TWO_DAY_SECONDS, JSON.toJSONString(dim));
    }
}
